<template>
  <div class="worker-test-event">
    <h1>{{ message }}</h1>
    <h3>基础知识铺垫：</h3>
    <p>独立于JS引擎线程的子线程，不阻塞js线程，不能访问DOM和BOM对象。可以进行XMLHttpRequest i/o请求(支持fetch api)，但是responseXML 、 channel这两个值会一直是null，有Location和navigator的只读访问。</p>
    <p>参考链接:https://segmentfault.com/a/1190000012925872</p>
    <section>
        <h3>1、Dedicated Workers</h3>
        <p>
          专享worker只作用于当前创建他的脚本。
        </p>
        <p>
          创建一个worker 只要 new 一个 构造函数即可，主线程与worker子线程通过postmessage 和 onmessage 进行通信。
        </p>
        <code>
          <pre>
			let myWorker = new Worker('./web.worker.js');
			myWorker.postMessage([1, 2]);//告诉worker线程做一些事情， 
			myWorker.onmessage = function(e) {
				// do something 收到worker消息后要处理的事情 
				console.error('我收到了消息：', e);
			}
          </pre>
        </code>
		<p>创建 web.worker.js</p>
		<code>
			<pre>
			onmessage = function(e) {
				// 以下内容不会被打印出来
				console.log('Worker: Message received from main script');
				let result = e.data[0] * e.data[1];
				if (isNaN(result)) {
					postMessage('Please write two numbers');
				} else {
					let workerResult = 'Result: ' + result;
					postMessage(workerResult);
				}
			}
			</pre>
		</code>
		<p>messageEvent 参考链接: http://www.php.cn/html5-tutorial-356983.html</p>
		<h4>我们来测试一哈:</h4>
		<div class="flex">
			<div class="left">
				<label>数字1:</label><input v-model="num1"/>
				<br/>
				<label>数字2:</label><input v-model="num2"/>
				<br/>
				<p class="btn-group">
					<span class="btn" @click="tosum">计算</span>
				</p>
				<p class="btn-group">
					<span class="btn" @click="closesum">关闭worker</span>
				</p>
			</div>
			<div class="right">
				<p>我是worker计算的结果：</p>
				<p>{{sum}}</p>
			</div>
		</div>
		<p>关闭worker有两种方式</p>
		<code>
			<pre>
			// js 线程主动关闭触发
			this.worker.terminate();

			// worker 中关闭 （self 相当于指向当前worker，类似js中的this）
			self.close();
			</pre>
		</code>
		<p>点击上面的“关闭worker“按钮试一下，再去输入数字，点击计算，worker不会给出任何反应</p>
		<h4>调试一般两种方式：</h4>
		<ol>
			<li>通过postmessage 发送给js主线程去console.log</li>
			<li>可以直接在worker文件打断点</li>
		</ol>
    </section>
	<section>
		<h3>2、Shared Workers（测试以下功能可以尝试打开多tab）</h3>
		<p>同域名的多个tab可应该用一个worker，可以实现多tab数据共享。</p>
		<p>
          创建一个worker 只要 new 一个 构造函数即可，主线程与worker子线程通过port 进行信息传递（postmessage，onmessage）。
        </p>
		<code>
			<pre>
			let myWorker = new SharedWorker('share.worker.js');// 第二个参数是线程的名字

			this.shareworker.port.postMessage([1, 2]);//告诉worker线程做一些事情， 
			this.shareworker.port.onmessage = function(e) {
				// do something 收到worker消息后要处理的事情 
				console.error('我是sharedworker:', e.data);
			}

			// 也可以用以下方式
			this.shareworker.port.addEventListener('message', e => {
				// doSomething
			});
			this.shareworker.port.start();
			</pre>
		</code>
		<p>创建share.worker.js</p>
		<code>
			<pre>
			let i = 1;
			let ports = {};
			onconnect = function(e) { 
				let port = e.ports[0];
				console.error(e);
				ports[`${e.target.name}${i}`] = port;
				port.postMessage(`这是共享的数据:${e.data}，我是第${i}个窗口`); 
				
				port.onmessage = e =>{
					for(let key in ports) {
						ports[key].postMessage(e.data);
					}
				}
				i += 1;

				// 也可以是下面写法
				port.addEventListener('message' , function(e) {
					// do something 收到worker消息后要处理的事情 
				});
				// *** 用addEventListener 方式必须要调start方法
				port.start();
			}
			</pre>
		</code>
		<h4>测试一下我们的代码（打开多tab尝试）</h4>
		<div class="flex">
			<div class="left">
				<input v-model="sharedNum1"/>
				<p class="btn-group">
					<span class="btn" @click="toshare">发给其他tab</span>
				</p>
				<p class="btn-group">
					<span class="btn" @click="closeshared">关闭worker</span>
				</p>
			</div>
			<div class="right">
				<p>以下是shared worker 共享的信息：</p>
				<p>{{sharedMessage}}</p>
			</div>
		</div>

		<h4>如果多个开了多个tab，直接更新sharedworker.js 无效，想要更新worker,有两种方式：</h4>
		<ol>
			<li>只留一个tab,刷新页面</li>
			<li>js主动关闭worker,（在调试过程中也可以用调制工具直接terminate）</li>
		</ol>
		<h4>想要关闭shared worker,有两种方式：</h4>
		<ol>
			<li>像本文用的这种假关闭，只是remove 了message的回调，实际上该tab还是会接受来自worker的数据，只是不触发任何动作</li>
			<li>通过postMessage告诉要关闭当前链接，在worker中把当前tab对应的port移除，不再向当前tab发送消息</li>
		</ol>
		<h4>调试方法</h4>
		<ol>
			<li>通过postmessage 发送给js主线程去console.log</li>
			<li>可以直接在worker文件打断点（需要打开chrome自带的worker单独调试devtools）先打开tab，输入：chrome://inspect/ ，
				会弹出DevTools，选择shared workers，继续找到我们想调试的share.worker.js，点击inspect，会弹出相应的调试工具，用法和js 的debugger一样，
				terminate 按钮是停掉所选择的worker。</li>
		</ol>
		<img class="img-shadow" src="../../images/somethingInterssting/worker/devtoolsshared.jpg"/>
		<img class="img-shadow" src="../../images/somethingInterssting/worker/debuggershared.jpg"/>
	</section>
	<section>
		<h3>3、Service Worker（东西太多，之后单独做一个笔记）</h3>
		<p>
			Service Worker和shareworker一样可以被通域名的多tab共同访问，他们的不同在于生命周期不同，
			shareworker在关联的页面全部关闭后就结束了，但是seviceworker可以一直驻留在后台。 不支持XMLHttpRequest,locatstorage，支持fetch API.
		</p>
		<ol>
			<li>Service Worker 必须和整个应用文件同域名</li>
			<li>Service Worker 必须放在应用根目录才能控制整站</li>
		</ol>
		<p>生命周期：install -> installed -> actvating -> Active -> Activated -> Redundant</p>
		<p>简单注册</p>
		<code>
			<pre>
				navigator.serviceWorker.register('./sevice.worker.js')
				.then(registration => console.error('ServiceWorker 注册成功 '))
				.catch(err => console.error('ServiceWorker 注册失败: ', err));

				navigator.serviceWorker.addEventListener('message',data=>{
					console.error('result:',data);
				});

				// worker中逻辑

				self.clients.matchAll({includeUncontrolled:true}).then(function(clients) {
					clients.forEach(client => {
						client.postMessage('value'); 
					});
				});
			</pre>
		</code>
		<h4>调试方法</h4>
		<ol>
			<li>通过postmessage 发送给js主线程去console.log</li>
			<li>可以直接在worker文件打断点（需要打开chrome自带的worker单独调试devtools）先打开tab，输入：chrome://inspect/ ，
				会弹出DevTools，选择shared workers，继续找到我们想调试的worker文件，点击inspect，会弹出相应的调试工具，用法和js 的debugger一样，
				terminate 按钮是停掉所选择的worker。（可参考shareworker）</li>
			<li>
				也可以直接打开devtools，在Application下找到Service Workers ，这里可以找到注册的所有services worker,找到要挑事的文件点进去也可以调试，同时也可以在这测试push等，如下图：
			</li>
			<li>
				或者直接在source中找对应的worker文件；
			</li>
		</ol>
		<img class="img-shadow" src="../../images/somethingInterssting/worker/push.jpg"/>
	</section>
    <p class="page-end">The End</p>
  </div>
</template>

<script lang="ts">
    import Vue from 'vue';
    import Component from 'vue-class-component';
	declare namespace SharedWorker {
		interface AbstractWorker extends EventTarget {
			onerror: (ev: ErrorEvent) => any;
		}

		export interface SharedWorker extends AbstractWorker {
			/**
			 * the value it was assigned by the object's constructor.
			 * It represents the MessagePort for communicating with the shared worker.
			 * @type {MessagePort}
			 */
			port: MessagePort;
		}
	}

	declare var SharedWorker: {
		prototype: SharedWorker.SharedWorker;
		/***
		 *
		 * @param {string} stringUrl    Pathname to JavaScript file
		 * @param {string} name         Name of the worker to execute
		 */
		new (stringUrl: string, name?: string): SharedWorker.SharedWorker;
	};
    @Component({
        // 所有的组件选项都可以放在这里
    })
    export default class worker extends Vue {
        // data 在这
		abc = '';
        message: string = 'worker、SharedWorker 和 Service Worker 对比'

		worker: any = null;
		num2: string|number = ''
		num1: string|number = ''
		sum: string|number = ''
		tosum () : void {
			this.worker.postMessage([this.num1, this.num2]);//告诉worker线程做一些事情，
		}
		closesum (): void {
			this.worker.terminate();
		}

		shareworker: any = null
		sharedNum1: string = ''
		sharedMessage: string =''
		toshare (): void {
			this.sharedNum1 && this.shareworker.port.postMessage([this.sharedNum1]);//告诉worker线程做一些事情， 
		}

		testSharedWorker (e): void {
			console.error('我是sharedworker:', e.data);
			this.sharedMessage = e.data;
		}

		closeshared (): void {
			this.shareworker.port.removeEventListener('message', this.testSharedWorker);
		}
        created () {
			this.worker = new Worker('./web.worker.js'); 
			this.worker.onmessage = (e) => {
				console.error('我收到了消息：', e);
				this.sum = e.data;
			}

			this.shareworker = new SharedWorker('share.worker.js', 'daihaiqing');
			this.shareworker.port.postMessage([1, 2]);//告诉worker线程做一些事情， 
			this.shareworker.port.addEventListener('message', this.testSharedWorker);
			this.shareworker.port.start();


			// navigator.serviceWorker.register('./sevice.worker.js')
			// .then(registration => console.error('ServiceWorker 注册成功 '))
			// .catch(err => console.error('ServiceWorker 注册失败: ', err));
			// navigator.serviceWorker.addEventListener('message',data=>{
			// 	console.error('result:',data);
			// });
        }
    }
</script>
<style lang="scss">
.worker-test-event{
	.left{
		padding: 20px;
		box-sizing: border-box;
		background: #f4f4f4;
		text-align: center;
		label{
			display: inline-block;
			width: 70px;
			vertical-align: middle;
		}
		input{
			vertical-align: middle;
		}
		.btn{
			display: inline-block;
			margin: 0 auto;
		}
	}
	.right{
		padding: 20px;
		text-align: center;
		font-size: 20px;
		font-weight: 800;
	}
	.btn-group{
		text-align: center;
	}
	.img-shadow{
		margin-bottom: 10px;
	}
}
</style>